﻿using System;
using System.Collections.Generic;
using System.Text;
using Yorrick.FlickrMetadataSynchr.Properties;
using System.Globalization;

namespace Yorrick.FlickrMetadataSynchr.Local
{
    /// <summary>
    /// Class that identifies a GPS version. A GPS version is a way of encoding GPS information in EXIF data.
    /// </summary>
    internal class GpsVersion
    {
        /// <summary>
        /// Represents the "null" version.
        /// </summary>
        private static readonly byte[] _NullGpsVersionBytes = new byte[NumberOfBytes]
            {
                0, 0, 0, 0
            };

        /// <summary>
        /// The byte sequence to indicate the supported GPS version.
        /// </summary>
        private static readonly byte[] _NormalGpsVersionBytes = new byte[NumberOfBytes]
            {
                2, 2, 0, 0
            };

        /// <summary>
        /// The byte sequence to indicate the "strange" GPS version.
        /// </summary>
        private static readonly byte[] _AlternativeGpsVersionBytes = new byte[NumberOfBytes]
            {
                0, 0, 2, 2
            };

        /// <summary>
        /// The value of bytes in a GPS version.
        /// </summary>
        private const int NumberOfBytes = 4;


        /// <summary>
        /// The bytes identifying the GPS version.
        /// </summary>
        public byte[] Bytes { get; private set; }

        /// <summary>
        /// Represents a normal GPS version.
        /// </summary>
        public static readonly GpsVersion Normal = new GpsVersion(_NormalGpsVersionBytes);

        /// <summary>
        /// Represents the alternative GPS version.
        /// </summary>
        public static readonly GpsVersion Alternative = new GpsVersion(_AlternativeGpsVersionBytes);

        /// <summary>
        /// Represents the null GPS version.
        /// </summary>
        public static readonly GpsVersion Null = new GpsVersion(_NullGpsVersionBytes);

        /// <summary>
        /// Constructs a new instance of <see cref="GpsVersion"/>.
        /// </summary>
        /// <param name="versionBytes">The byte sequence identifying the version.</param>
        internal GpsVersion(byte[] versionBytes)
        {
            if (versionBytes == null)
            {
                versionBytes = _NullGpsVersionBytes;
            }

            if (versionBytes.Length != NumberOfBytes)
            {
                throw new ArgumentException(Messages.GpsVersionShouldHaveFourBytes, "versionBytes");
            }

            Bytes = versionBytes;
        }

        /// <summary>
        /// Compares two byte arrays to determine if they represent the same GPS version.
        /// </summary>
        /// <param name="version1">The first byte array.</param>
        /// <param name="version2">The second byte array.</param>
        /// <returns><c>true</c> if the bytes arrays have the same length and content, <c>false</c> otherwise.</returns>
        public bool Equals(GpsVersion version2)
        {
            if (this.Bytes.Length != version2.Bytes.Length)
            {
                return false;
            }
            for (int byteCounter = 0; byteCounter < this.Bytes.Length; byteCounter++)
            {
                if (this.Bytes[byteCounter] != version2.Bytes[byteCounter])
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Gets whether this GPS version is supported.
        /// </summary>
        public bool IsSupportedVersion
        {
            get
            {
                bool isSupported = this.Equals(GpsVersion.Normal) || this.Equals(GpsVersion.Alternative) || this.Equals(GpsVersion.Null);

                return isSupported;
            }
        }

        /// <summary>
        /// Compares the specified object to this instance.
        /// </summary>
        /// <param name="obj">The <see cref="Object"/> to compare to.</param>
        /// <returns><c>true</c> if the object is a byte array and the bytes arrays have the same length and content, <c>false</c> otherwise.</returns>
        public override bool Equals(object obj)
        {
            GpsVersion version2 = obj as GpsVersion;

            if (version2 == null)
            {
                return false;
            }
            else
            {
                return Equals(version2);
            }
        }

        /// <summary>
        /// Returns the hash code for the current instance.
        /// </summary>
        /// <returns>The hash code for the current instance.</returns>
        public override int GetHashCode()
        {
            string bytesString =
                Convert.ToString(Bytes[0], CultureInfo.InvariantCulture)
                + Convert.ToString(Bytes[1], CultureInfo.InvariantCulture)
                + Convert.ToString(Bytes[2], CultureInfo.InvariantCulture)
                + Convert.ToString(Bytes[3], CultureInfo.InvariantCulture);

            return bytesString.GetHashCode();
        }
    }
}
